﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TISModel;
using BTO;
using DAL;
using Exceptions;


namespace BLL
{
    public class BLLReserva
    {
        public List<BTO.Reserva> list()
        {
            DALReserva reservas = new DALReserva();
            return reservas.list();
        }

        public BTO.Reserva getByID(int id)
        {
            DALReserva dalReserva = new DALReserva();
            BTO.Reserva btoReserva = dalReserva.getByID(id);

            if (btoReserva == null)
            {
                throw new NotFoundReservaException("Reserva não encontrada!");
            }

            return btoReserva;

        }

        public List<BTO.Reserva> getByIDChegada(int id)
        {
            DALReserva dalReserva = new DALReserva();
            List<BTO.Reserva> btoReserva = dalReserva.getByIDChegada(id);

            if (btoReserva == null)
            {
                throw new NotFoundReservaException("Nenhuma reserva com este destino!");
            }

            return btoReserva;

        }

        public List<BTO.Reserva> getByIDPartida(int id)
        {
            DALReserva dalReserva = new DALReserva();
            List<BTO.Reserva> btoReserva = dalReserva.getByIDPartida(id);

            if (btoReserva == null)
            {
                throw new NotFoundReservaException("Nenhuma reserva com esta origem!");
            }

            return btoReserva;

        }

        public List<BTO.Reserva> getByIDTransporteIda(int id)
        {
            DALReserva dalReserva = new DALReserva();
            List<BTO.Reserva> btoReserva = dalReserva.getByIDTransporteIda(id);

            if (btoReserva == null)
            {
                throw new NotFoundReservaException("Nenhuma reserva com este transporte de ida!");
            }

            return btoReserva;

        }

        public List<BTO.Reserva> getByIDTransporteVolta(int? id)
        {
            DALReserva dalReserva = new DALReserva();
            List<BTO.Reserva> btoReserva = dalReserva.getByIDTransporteVolta(id);

            if (btoReserva == null)
            {
                throw new NotFoundReservaException("Nenhuma reserva com este transporte de regresso!");
            }

            return btoReserva;

        }

        public List<BTO.Reserva> getByCliente(int idCliente)
        {
            DALReserva dalReserva = new DALReserva();
            List<BTO.Reserva> btoReserva = dalReserva.getByCliente(idCliente);

            if (btoReserva == null)
            {
                throw new NotFoundClienteReservasException("Este cliente não tem reservas!");
            }

            return btoReserva;
        }

        public List<BTO.Reserva> getByAgencia(int idAgencia)
        {
            DALReserva dalReserva = new DALReserva();
            List<BTO.Reserva> btoReserva = dalReserva.getByAgencia(idAgencia);

            if (btoReserva.Count() == 0)
            {
                throw new NotFoundAgenciaReservasException("Nao foram registadas reservas nesta agencia.");
            }

            return btoReserva;
        }

        public List<BTO.Reserva> getByDataReserva(String dataReserva)
        {
            DALReserva dalReserva = new DALReserva();
            List<BTO.Reserva> btoReserva = dalReserva.getByDataReserva(DateTime.Parse(dataReserva));

            if (btoReserva == null)
            {
                throw new NotFoundDataReservasException("Não existem reservas feitasnesta data!");
            }

            return btoReserva;
        }

        public List<BTO.Reserva> getByDataPartida(String data)
        {
            DALReserva dalReserva = new DALReserva();
            List<BTO.Reserva> btoReserva = dalReserva.getByDataPartida(DateTime.Parse(data));

            if (btoReserva == null)
            {
                throw new NotFoundDataReservasException("Não existem reservas com partidas nesta data!");
            }

            return btoReserva;
        }

        public List<BTO.Reserva> getByDataChegada(String data)
        {
            DALReserva dalReserva = new DALReserva();
            List<BTO.Reserva> btoReserva = dalReserva.getByDataChegada(DateTime.Parse(data));

            if (btoReserva == null)
            {
                throw new NotFoundDataReservasException("Não existem reservas com chegadas nesta data!");
            }

            return btoReserva;
        }

        public List<BTO.Reserva> getByAlojamento(int idAlojamento)
        {
            DALReserva dalReserva = new DALReserva();
            List<BTO.Reserva> btoReserva = dalReserva.getByIDAlojamento(idAlojamento);

            if (btoReserva == null)
            {
                throw new NotFoundAlojamentoException("Não existem reservas para esse alojamento!");
            }

            return btoReserva;
        }

        public void insert(BTO.Reserva toInsert)
        {
            DALReserva dalReserva = new DALReserva();
            BLLTransporte bllTransporte = new BLLTransporte();       

            //Selecciona dados dos transportes escolhidos
            BTO.Transporte transporteIdaEscolhido = bllTransporte.getByID(toInsert.IDTransporteIda);

            //Validar IDA
            try
            {
                TransporteLotacaoIda(toInsert.IDTransporteIda, toInsert.dataPartida.ToString(), toInsert.nrPessoas);
            }
            catch (TransporteLotacaoException e)
            {
                throw new TransporteLotacaoException(e.Message);
            }
            catch (Exception)
            {
                throw new Exception("Não foi possível fazer a reserva com o transporte de ida indicado! ");
            }

            //Validar Volta
            try
            {
                TransporteLotacaoVolta(toInsert.IDTransporteIda, toInsert.dataPartida.ToString(), toInsert.nrPessoas);
            }
            catch (TransporteLotacaoException e)
            {
                throw new TransporteLotacaoException(e.Message);
            }
            catch (Exception)
            {
                throw new Exception("Não foi possível fazer a reserva com o transporte de Volta indicado! ");
            }

            //Validar Alojamento
            try
            {
                AlojamentoLotacao(toInsert.IDAlojamento, toInsert.dataPartida.ToString(), toInsert.nrPessoas);
            }
            catch (AlojamentoLotacaoException e)
            {
                throw new AlojamentoLotacaoException(e.Message);
            }
            catch (Exception)
            {
                throw new Exception("Não foi possível fazer a reserva com o alojamento indicado! ");
            }
            


            try
            {
                dalReserva.insert(toInsert);
            }
            catch (Exception e)
            {
                throw new InsertReservaException(e.Message);
            }
        }       

        public void update(BTO.Reserva toUpdate)
        {
            DALReserva dalReserva = new DALReserva();
            BLLTransporte bllTransporte = new BLLTransporte();

            //Selecciona dados dos transportes escolhidos
            BTO.Transporte transporteIdaEscolhido = bllTransporte.getByID(toUpdate.IDTransporteIda);

            //Validar IDA
            try
            {
                TransporteLotacaoIda(toUpdate.IDReserva, toUpdate.IDTransporteIda, toUpdate.dataPartida.ToString(), toUpdate.nrPessoas);
            }
            catch (TransporteLotacaoException e)
            {
                throw new TransporteLotacaoException(e.Message);
            }
            catch (Exception)
            {
                throw new Exception("Não foi possível actualizar a reserva com o transporte de ida indicado! ");
            }

            //Validar Volta
            try
            {
                TransporteLotacaoVolta(toUpdate.IDReserva, toUpdate.IDTransporteIda, toUpdate.dataPartida.ToString(), toUpdate.nrPessoas);
            }
            catch (TransporteLotacaoException e)
            {
                throw new TransporteLotacaoException(e.Message);
            }
            catch (Exception)
            {
                throw new Exception("Não foi possível actualizar a reserva com o transporte de Volta indicado! ");
            }

            //Validar Alojamento

            try
            {
                AlojamentoLotacao(toUpdate.IDReserva, toUpdate.IDAlojamento, toUpdate.dataPartida.ToString(), toUpdate.nrPessoas);
            }
            catch (AlojamentoLotacaoException e)
            {
                throw new AlojamentoLotacaoException(e.Message);
            }
            catch (Exception)
            {
                throw new UpdateReservaException("Não foi possível actualizar a reserva com o alojamento indicado! ");
            }

            try
            {
                dalReserva.update(toUpdate);
            }
            catch (Exception e)
            {
                throw new UpdateReservaException(e.Message);
            }
        }

        public void delete(int id)
        {
            DALReserva dalReserva = new DALReserva();
            DALContacto dalContacto = new DALContacto();

            try
            {
                dalReserva.delete(id);
            }
            catch (Exception e)
            {
                throw new DeleteReservaException(e.Message);
            }
        }
        
        private void TransporteLotacaoIda(int IDTransporteIda, String dataPartida, int nrPessoas)
        {
            BLLReserva bllReserva = new BLLReserva();
            BLLTransporte bllTransporte = new BLLTransporte();
            List<BTO.Reserva> btoReservasDataPartida;
            List<BTO.Reserva> btoReservasTransporteIda = new List<BTO.Reserva>();
            int lotacaoActualIda = 0;

            //Selecciona dados dos transportes escolhidos
            BTO.Transporte transporteIdaEscolhido = bllTransporte.getByID(IDTransporteIda);

            //Selecciona reservas do mesmo dia
            btoReservasDataPartida = bllReserva.getByDataPartida(dataPartida);

            //IDA
            //Selecciona das reservas do mesmo dia as que tem o mesmo transporte de ida
            foreach (BTO.Reserva btoReserva in btoReservasDataPartida)
            {
                if (btoReserva.IDTransporteIda == IDTransporteIda)
                {
                    btoReservasTransporteIda.Add(btoReserva);
                }
            }

            //Soma a lotação actual com a que é requerida na reserva
            foreach (BTO.Reserva btoReserva in btoReservasTransporteIda)
            {
                lotacaoActualIda += btoReserva.nrPessoas;
            }

            //Se Lotação actual do transporte para determinado dia + a que é requerida na reserva actual > lotação do transporte
            if ((lotacaoActualIda + nrPessoas) > transporteIdaEscolhido.lotacao)
            {
                throw new TransporteLotacaoException("Existem " + (transporteIdaEscolhido.lotacao - lotacaoActualIda) + " lugares disponíveis de regresso neste transporte e nesta data!");
            }
        }

        private void TransporteLotacaoIda(int IDReserva, int IDTransporteIda, String dataPartida, int nrPessoas)
        {
            BLLReserva bllReserva = new BLLReserva();
            BLLTransporte bllTransporte = new BLLTransporte();
            List<BTO.Reserva> btoReservasDataPartida;
            List<BTO.Reserva> btoReservasTransporteIda = new List<BTO.Reserva>();
            int lotacaoActualIda = 0;

            //Selecciona dados dos transportes escolhidos
            BTO.Transporte transporteIdaEscolhido = bllTransporte.getByID(IDTransporteIda);

            //Selecciona reservas do mesmo dia
            btoReservasDataPartida = bllReserva.getByDataPartida(dataPartida);

            //IDA
            //Selecciona das reservas do mesmo dia as que tem o mesmo transporte de ida
            foreach (BTO.Reserva btoReserva in btoReservasDataPartida)
            {
                if (btoReserva.IDTransporteIda == IDTransporteIda && btoReserva.IDReserva!=IDReserva)
                {
                    btoReservasTransporteIda.Add(btoReserva);
                }
            }

            //Soma a lotação actual com a que é requerida na reserva
            foreach (BTO.Reserva btoReserva in btoReservasTransporteIda)
            {
                lotacaoActualIda += btoReserva.nrPessoas;
            }

            //Se Lotação actual do transporte para determinado dia + a que é requerida na reserva actual > lotação do transporte
            if ((lotacaoActualIda + nrPessoas) > transporteIdaEscolhido.lotacao)
            {
                throw new TransporteLotacaoException("Existem " + (transporteIdaEscolhido.lotacao - lotacaoActualIda) + " lugares disponíveis de regresso neste transporte e nesta data!");
            }
        }

        private void TransporteLotacaoVolta(int? IDTransporteVolta, String dataChegada, int nrPessoas)
        {
            BLLReserva bllReserva = new BLLReserva();
            BLLTransporte bllTransporte = new BLLTransporte();
            List<BTO.Reserva> btoReservasDataChegada;
            List<BTO.Reserva> btoReservasTransporteVolta = new List<BTO.Reserva>();
            int lotacaoActualVolta = 0;

            //Selecciona dados dos transportes escolhidos
            BTO.Transporte transporteIdaEscolhido = bllTransporte.getByID(IDTransporteVolta);

            //Selecciona reservas do mesmo dia
            btoReservasDataChegada = bllReserva.getByDataPartida(dataChegada);

            //IDA
            //Selecciona das reservas do mesmo dia as que tem o mesmo transporte de ida
            foreach (BTO.Reserva btoReserva in btoReservasDataChegada)
            {
                if (btoReserva.IDTransporteVolta == IDTransporteVolta)
                {
                    btoReservasTransporteVolta.Add(btoReserva);
                }
            }

            //Soma a lotação actual com a que é requerida na reserva
            foreach (BTO.Reserva btoReserva in btoReservasTransporteVolta)
            {
                lotacaoActualVolta += btoReserva.nrPessoas;
            }

            //Se Lotação actual do transporte para determinado dia + a que é requerida na reserva actual > lotação do transporte
            if ((lotacaoActualVolta + nrPessoas) > transporteIdaEscolhido.lotacao)
            {
                throw new TransporteLotacaoException("Existem " + (transporteIdaEscolhido.lotacao - lotacaoActualVolta) + " lugares disponíveis de regresso neste transporte e nesta data!");
            }
        }

        private void TransporteLotacaoVolta(int IDReserva, int? IDTransporteVolta, String dataChegada, int nrPessoas)
        {
            BLLReserva bllReserva = new BLLReserva();
            BLLTransporte bllTransporte = new BLLTransporte();
            List<BTO.Reserva> btoReservasDataChegada;
            List<BTO.Reserva> btoReservasTransporteVolta = new List<BTO.Reserva>();
            int lotacaoActualVolta = 0;

            //Selecciona dados dos transportes escolhidos
            BTO.Transporte transporteIdaEscolhido = bllTransporte.getByID(IDTransporteVolta);

            //Selecciona reservas do mesmo dia
            btoReservasDataChegada = bllReserva.getByDataPartida(dataChegada);

            //IDA
            //Selecciona das reservas do mesmo dia as que tem o mesmo transporte de ida
            foreach (BTO.Reserva btoReserva in btoReservasDataChegada)
            {
                if (btoReserva.IDTransporteVolta == IDTransporteVolta && btoReserva.IDReserva != IDReserva)
                {
                    btoReservasTransporteVolta.Add(btoReserva);
                }
            }

            //Soma a lotação actual com a que é requerida na reserva
            foreach (BTO.Reserva btoReserva in btoReservasTransporteVolta)
            {
                lotacaoActualVolta += btoReserva.nrPessoas;
            }

            //Se Lotação actual do transporte para determinado dia + a que é requerida na reserva actual > lotação do transporte
            if ((lotacaoActualVolta + nrPessoas) > transporteIdaEscolhido.lotacao)
            {
                throw new TransporteLotacaoException("Existem " + (transporteIdaEscolhido.lotacao - lotacaoActualVolta) + " lugares disponíveis de regresso neste transporte e nesta data!");
            }
        }

        private void AlojamentoLotacao(int? IDAlojamento, String data, int nrPessoas)
        {
            BLLReserva bllReserva = new BLLReserva();
            BLLAlojamento bllAlojamento = new BLLAlojamento();
            List<BTO.Reserva> btoReservasAlojamentosData;
            List<BTO.Reserva> btoReservasAlojamento = new List<BTO.Reserva>();
            int lotacaoActual = 0;

            //Selecciona dado do alojamento escolhido
            BTO.Alojamento AlojamentoEscolhido = bllAlojamento.getByID(IDAlojamento);

            //Selecciona reservas do mesmo dia
            btoReservasAlojamentosData = bllReserva.getByDataPartida(data);

            //IDA
            //Selecciona das reservas do mesmo dia as que tem o mesmo alojamento

            foreach (BTO.Reserva btoReserva in btoReservasAlojamentosData)
            {
                if (btoReserva.IDAlojamento == IDAlojamento)
                {
                    btoReservasAlojamento.Add(btoReserva);
                }
            }

            //Soma a lotação actual com a que é requerida na reserva
            foreach (BTO.Reserva btoReserva in btoReservasAlojamento)
            {
                lotacaoActual += btoReserva.nrPessoas;
            }

            //Se Lotação actual do alojamento para determinado dia + a que é requerida na reserva actual > lotação do alojamento
            if ((lotacaoActual + nrPessoas) > AlojamentoEscolhido.lotacao)
            {
                throw new AlojamentoLotacaoException("Existem " + (AlojamentoEscolhido.lotacao - lotacaoActual) + " lugares disponíveis neste alojamento e nesta data!");
            }
        }

        private void AlojamentoLotacao(int IDReserva, int? IDAlojamento, String data, int nrPessoas)
        {
            BLLReserva bllReserva = new BLLReserva();
            BLLAlojamento bllAlojamento = new BLLAlojamento();
            List<BTO.Reserva> btoReservasAlojamentosData;
            List<BTO.Reserva> btoReservasAlojamento = new List<BTO.Reserva>();
            int lotacaoActual = 0;

            //Selecciona dado do alojamento escolhido
            BTO.Alojamento AlojamentoEscolhido = bllAlojamento.getByID(IDAlojamento);

            //Selecciona reservas do mesmo dia
            btoReservasAlojamentosData = bllReserva.getByDataPartida(data);

            //IDA
            //Selecciona das reservas do mesmo dia as que tem o mesmo alojamento

            foreach (BTO.Reserva btoReserva in btoReservasAlojamentosData)
            {
                if (btoReserva.IDAlojamento == IDAlojamento && btoReserva.IDReserva != IDReserva)
                {
                    btoReservasAlojamento.Add(btoReserva);
                }
            }

            //Soma a lotação actual com a que é requerida na reserva
            foreach (BTO.Reserva btoReserva in btoReservasAlojamento)
            {
                lotacaoActual += btoReserva.nrPessoas;
            }

            //Se Lotação actual do alojamento para determinado dia + a que é requerida na reserva actual > lotação do alojamento
            if ((lotacaoActual + nrPessoas) > AlojamentoEscolhido.lotacao)
            {
                throw new AlojamentoLotacaoException("Existem " + (AlojamentoEscolhido.lotacao - lotacaoActual) + " lugares disponíveis neste alojamento e nesta data!");
            }
        }
    }
}
